'use strict';
const router = require('koa-router')()
const xlsx = require('node-xlsx')
const aws = require('../service/aws')
const asyncBusboy = require('async-busboy')
const moment = require('moment')
const middle = require('../middlewares')
const fs = require('fs')
const util = require('util')
const config = require('../config')
const rp = require('request-promise')
const sqs = require('../service/sqs')
const crypto = require('crypto')
const _ = require('../util/util')
const fdnServer = require('../service/foundation')
const cache = require('../service/cache')



const mdisp = config.MDISP_PREFIX
const mdisp_token = config.MDISP_TOKEN
const {
    Ledger,
    OriginalLedger,
    LedgerResult,
    LedgerTag,
    sequelize
    } = require('../models')
sequelize.sync()


! async function () {
    await sqs.createQueue('ledger')
    const consumer = sqs.consumer('ledger', async function (message,done) {
        const body = JSON.parse(message.Body)
        //console.log(body)
        const qs = {}
        if(body.name){
            qs.name = body.name
        }
        if(body.manufacturer){
            qs.manufacturer = body.manufacturer
        }
        if(body.brand){
            qs.brand = body.brand
        }
        if(JSON.stringify(qs) === '{}'){
            await Ledger.update({status: 'not_matched',uodated_by: 0},{where:{id:body.id}})
            return done()
        }
        qs.limit = 1
        const options = {
            uri: `${mdisp}/spu`,
            qs: qs,
            headers: {
                'User-Agent': 'Request-Promise',
                token: mdisp_token
            },
            json: true
        }
        const spu = await rp(options)
        if(spu.data.length === 0){
            await Ledger.update({status: 'not_matched',uodated_by: 0},{where:{id:body.id}})
            return done()
        }
        await Ledger.update({spu_id: spu.data.items[0].spuId, generic: spu.data.items[0].common_name, status: 'matched',uodated_by: 0},{where:{id:body.id}})

        //@todo match spu
        done()
    })
    consumer.on('stoped', function(){
        console.log('stoped')
    })
    consumer.start()
} ()


router.post('/organization/:organizationId(\\d{1,20})', async function (ctx, next) {
    const { organizationId } = ctx.params
    const { files, fields } = await asyncBusboy(ctx.req)
    const { arr, userId, token, dealType } = fields

    if(dealType === 'delete'){
        await Ledger.destroy({where:{organization_id: organizationId}})
        await OriginalLedger.destroy({where:{organization_id: organizationId}})
        await LedgerResult.update({status:'giveup'},{where:{organization_id: organizationId}})
    }

    const handling = await LedgerResult.findOne({
        where: {
            organization_id: organizationId,
            status: 'handling'
        }
    })
    if (handling) {
        return ctx.throw(400, 'LEDGER_LEDGER_IN_HANDLING')
    }

    if (!/\.xlsx/.test(files[0].filename)) {
        return ctx.throw(400, 'LEDGER_FILE_INVALID')
    }
    const key = crypto.createHash('md5').update(`${files[0].path}_${Date.now()}`).digest('hex')
    //const haskey = await LedgerResult.findOne({
    //    where: {
    //        organization_id: organizationId,
    //        key,
    //    }
    //})
    //if (haskey) {
    //    return ctx.throw(400, 'LEDGER_NO_SUPORT_REUPLOAD')
    //}


    const res = await LedgerResult.create({
        organization_id: organizationId,
        file_name: files[0].filename,
        key: key,
        user: userId,
    })
    const params = {arr, files, organizationId, userId, key, res, dealType}
    deal(params)
    ctx.send(res)
})


const deal = async (params) => {
    const {arr, files, organizationId, userId, key, res, dealType} = params
    const errdata = []
    let relations = []
    relations['user_defined'] = []
    let status = 'solved'
    let _arr
    let data
    try {
        _arr = JSON.parse(arr)
        for (let i = 0; i < _arr.length; i++) {
            if (_arr[i].field === 'user_defined') {
                relations['user_defined'].push(i)
                continue
            }
            relations[_arr[i].field] = {
                index: i,
                type: _arr[i].type
            }
        }
        data = xlsx.parse(files[0].path)[0].data;
        let tmp = data.shift()
        tmp.push('失败原因')
        errdata.push(tmp)
    } catch (err) {
        //console.log(err)
        status = 'invalid'
        //console.log(status)
        return await LedgerResult.update({status: status, mark: '解析出错'}, {where: {id: res.id}})

    }
    //console.log(relations)


    //@todo write database

    for (let i = 0; i < data.length; i++) {
        let ledger = {}
        let effective = true
        for (let key in relations) {
            if (key === 'user_defined') {
                let userDefined = []
                for (let j = 0; j < relations['user_defined'].length; j++) {
                    userDefined.push({
                        name: _arr[relations['user_defined'][j]].name,
                        data: data[i][relations['user_defined'][j]],
                        type: _arr[relations['user_defined'][j]].type,
                    })
                }
                ledger.user_defined = JSON.stringify(userDefined)
                continue
            }

            if(key === 'department'){
                if(!data[i][relations[key].index] || !data[i][relations[key].index]){
                    data[i].push('科室不能为空')
                    errdata.push(data[i])
                    effective = false
                    break
                }
            }

            if (key === 'tag'){
                const _tag = await fdnServer.getTagByTitle(data[i][relations[key].index])
                if(!_tag.data.tagItem){
                    data[i].push(`${key}: 不存在此标签`)
                    errdata.push(data[i])
                    effective = false
                    break
                }
                await cache.setTag(_tag.data.tagItem.id,_tag.data.tagItem)
                ledger['tagId'] = _tag.data.tagItem.id
            }
            if (relations[key].type === 'DATE') {
                let _time
                if (data[i][relations[key].index].match(/^((?:19|20)\d\d)-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])$/) || data[i][relations[key].index].match(/^(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])-((?:19|20)\d\d)$/)) {
                    _time = moment(data[i][relations[key].index], ["MM-DD-YYYY", "YYYY-MM-DD"]).format()
                }
                if (!_time) {
                    data[i].push('时间格式错误')
                    errdata.push(data[i])
                    effective = false
                    break
                }
                ledger[key] = _time
                continue
            }
            if (relations[key].type === 'INTEGER') {
                if (/^[0-9]+$/.test(data[i][relations[key].index])) {
                    ledger[key] = Number(data[i][relations[key].index])
                } else {
                    data[i].push(`${key}: 格式错误`)
                    errdata.push(data[i])
                    effective = false
                    break
                }
                continue
            }
            if (relations[key].type === 'FLOAT') {
                if (/^([1-9][\d]{0,7}|0)(\.[\d]{1,2})?$/.test(data[i][relations[key].index])) {
                    ledger[key] = Number(data[i][relations[key].index])
                } else {
                    data[i].push(`${key}: 格式错误`)
                    errdata.push(data[i])
                    effective = false
                    break
                }

                continue
            }
            ledger[key] = data[i][relations[key].index]

        }
        let md5 = crypto.createHash('md5').update(_.raw(ledger)).digest('hex')
        const isExist = await Ledger.findOne({where: {md5}})
        if(isExist && dealType !=='repeat' && dealType !== 'cover'){
            data[i].push(' 重复导入')
            errdata.push(data[i])
            continue
        }
        ledger['md5'] = md5
        ledger['organization_id'] = organizationId
        ledger['key'] = key
        ledger['created_by'] = userId
        if (effective) {
            let transaction = await sequelize.transaction()
            let message
            if(dealType === 'cover'){
                //@todo find?update:create

            }
            try {
                message = await Ledger.create(ledger, {transaction})
                await OriginalLedger.create(ledger, {transaction})
                if(ledger.tagId){
                    await LedgerTag.create({
                        ledger_id: message.id,
                        tag_id: ledger.tagId
                    },{transaction})
                }
                await transaction.commit()
            } catch (err) {
                console.log(err)
                await transaction.rollback()
                data[i].push('写入数据库错误')
                errdata.push(data[i])
                continue
            }
            sqs.sendMessage('ledger', message)

        }
    }
    //@todo upload to S3
    let s3Info
    const buffer = xlsx.build([{name: `err_ledger.xlsx`, data: errdata}])
    s3Info = await aws.put(buffer, `err_ledger.xlsx`, 'application/octet-stream')
    await LedgerResult.update({
        status: status,
        file_url: s3Info.Location,
        num: errdata.length - 1
    }, {where: {id: res.id}})
}

/**
 * result list
 */

router.get('/organization/:organizationId(\\d{1,20})/result', middle.pagination, async function (ctx, next) {
    const { params, state } = ctx
    const { organizationId } = params
    const { pagination } = state
    let rlts = await LedgerResult.$pagination(pagination, {
        where: {
            organization_id: organizationId
        },
        order: [['created_at', 'DESC']]
    })
    if(rlts.length === 0 ){
        return ctx.send({rlt})
    }
    let ids = []
    for(let i = 0; i< rlts.length; i++){
        ids.push(rlts[i].user)
    }
    ids = _.removeDup(ids)
    const userInfos = await fdnServer.getUserBatch(ids)
    const results = []
    for(let i = 0; i<rlts.length; i++){
        results.push(rlts[i].dataValues)
        results[i].user_info = null
        for(let j = 0; j <userInfos.users.length; j++){
            if(!userInfos.users[j])continue
            if(rlts[i].user == userInfos.users[j].id){
                results[i].user_info = userInfos.users[j]
                break
            }
        }
    }
    ctx.send({results, pagination})
})

/**
 * result by key
 */
router.get('/organization/:organizationId(\\d{1,20})/result/:key', async function (ctx, next) {
    const {organizationId, key} = ctx.params
    const result = await LedgerResult.findOne({
        where: {
            organization_id: organizationId,
            key,
        }
    })
    ctx.send({result})
})

/**
 * ledger list
 */
router.get('/organization/:organizationId(\\d{1,20})', middle.pagination, async function (ctx, next) {
    const { params, state} = ctx
    const { organizationId} = params
    const { pagination} = state

    const {department, tag, type, q} = ctx.query

    const where = {}
    const order = []
    const include = [{
        model: LedgerTag,
    }]

    where.organization_id = organizationId

    // department
    if( department && department.trim() ) {
        where.department = department
    }

    // type
    if( type && type.trim()) {
        where.type = type
    }

    if ( q ) {
        const $like = `%${q}%`

        where.$or = [{
            department: {$like}
        }, {
            name: {$like}
        }, {
            brand: {$like}
        }, {
            asset_number: {$like}
        }, {
            model_number: {$like}
        }, {
            serial_number: {$like}
        }, {
            manufacturer: {$like}
        }]
    }

    //tag
    if(tag){
        include[0].where = {
            tag_id : tag
        }
    }

    const rlt = await Ledger.$pagination(pagination, {
        where,
        include,
        order,
        limit: 20
    })
    const ledgers = []
    for(let i=0; i< rlt.length; i++){
        const tempLedger = rlt[i].dataValues
        if(tempLedger.LedgerTags.length >0){
            for(let j = 0; j< tempLedger.LedgerTags.length; j++){
                const tempTag = tempLedger.LedgerTags[j].dataValues
                let tag = await cache.getTag(tempTag.tag_id)
                if(tag){
                    tag = JSON.parse(tag)
                    tempTag.title = tag.title
                }
                if(!tag){
                    tag = await fdnServer.getTagById(tempTag.tag_id)
                    if(tag){
                        await cache.setTag(tag.tag.id,tag.tag)
                    }
                    tempTag.title = tag.tag.title
                }
            }
        }
        ledgers.push(tempLedger)
    }

    ctx.send({ledgers, pagination})
})


/**
 * get ledger by id
 */
router.get('/organization/:organizationId(\\d{1,20})/:ledgerId(\\d{1,20})', async function(ctx, next) {
    const { organizationId, ledgerId } = ctx.params
    let ledger = await Ledger.findOne({
        where:{
            organization_id: organizationId,
            id: ledgerId
        },
        include: [{
            model: LedgerTag,
        }]
    })
    ledger = ledger.dataValues
    for(let j = 0; j< ledger.LedgerTags.length; j++){
        const tempTag = ledger.LedgerTags[j].dataValues
        let tag = await cache.getTag(tempTag.tag_id)
        if(tag){
            tag = JSON.parse(tag)
            tempTag.title = tag.title
        }
        if(!tag){
            tag = await fdnServer.getTagById(tempTag.tag_id)
            if(tag){
                await cache.setTag(tag.tag.id,tag.tag)
            }
            tempTag.title = tag.tag.title
        }
    }
    ctx.send({ledger})

})
/**
 * modify ledger
 */
router.put('/organization/:organizationId(\\d{1,20})/:ledgerId(\\d{1,20})', async function(ctx, next) {
    const { organizationId, ledgerId } = ctx.params
    const data = ctx.pick('asset_number','name','department','brand','manufacturer','model_number',
        'serial_number','memo','supplier','location','purchased_date','installDate', 'accept_date','years')

    const ledger = await Ledger.update(data,{where:{id: ledgerId,organization_id: organizationId}})

    ctx.send({ledger})

})

/**
 * eidt ledger batch
 */
router.post('/organization/:organizationId(\\d{1,20})/batch', async function (ctx, next){
    const {organizationId} = ctx.params
    const {ids,department,tag,type, value} = ctx.request.body
    let where = {}
    const data = {}
    if(ids){
        if (!util.isArray(ids)) {
            return ctx.throw(400, 'LERGER_IDS_SHOULD_AN_ARRAY')
        }
        if (ids.length === 0) {
            return ctx.throw(400, 'LERGER_IDS_INVALID_ARRAY')
        }
        where.id = {
            $in : ids
        }
    }else {
        if (department && department.trim()){
            where.department = department
        }
        if (tag && tag.trim()){
            where.tag = tag
        }
        if (type && type.trim()){
            where.type = type
        }
    }
    where.organization_id = organizationId
    if(!value || !value.trim()){
        return ctx.throw(400, 'LERGER_DEPARTMENTVALUE_REQUIRED')
    }
    data.department = value
    const rlt = await Ledger.update(data,{where})
    ctx.send({rlt})

})


/**
 * get department list
 */
router.get('/organization/:organizationId(\\d{1,20})/department', async function(ctx, next) {
    const {organizationId} = ctx.params
    const {userId} = ctx.query
    let departments = []

    let dpts = await Ledger.findAll({
        attributes: ['department'],
        where: {
            organization_id: organizationId,
            derpartment_confirmed: false
        },
        group: ['department']
    })
    for(let item in dpts){
        departments.push(dpts[item].department)
    }
    ctx.send({departments})
})


/**
 * add department batch
 */
router.post('/organization/:organizationId(\\d{1,20})/department/batch', async function(ctx, next) {
    const { organizationId } = ctx.params
    const { departments,change, userId } = ctx.request.body

    if ( departments && !util.isArray(departments) ) {
        return ctx.throw(400, 'LEDGER_DEPARTMENTS_SHOULD_ARRAY')
    }
    try{
        for(let i = 0; i<change.length; i++){
            if(change[i].past && change[i].now){
                await Ledger.update({updated_by: userId, department:change[i].now},{where:{department:change[i].past}})
            }
        }
    }catch(err){
        console.log(err)
        return ctx.send(400,'LEDGER_CHANGE_ERR')
    }


    for(let item in departments){
        let dpt
        dpt = await fdnServer.departmentQuery(departments[item],organizationId)
        if(!dpt.department){
            dpt = await fdnServer.departmentCreate(departments[item], organizationId)
        }
        dpt = await Ledger.update({updated_by: userId, department:dpt.department.id, derpartment_confirmed: true},{where: {department: departments[item],derpartment_confirmed:false}})

    }
    // @todo add alias
    for(let i = 0; i<change.length; i++){
        const dpt = await fdnServer.departmentQuery(change[i].now,organizationId)
        const alias = await fdnServer.departmentAliasCreate(dpt.department.id,change[i].past)
    }

    return ctx.send('ok')

})

/**
 * department list from fdn
 */
router.get('/departments', async function (ctx, next) {
    const {organizationId} = ctx.query
    if ( !organizationId ) {
        ctx.throw(400, 'ORGANIZATION_ID_REQUIRED')
        return
    }
    const departments = await fdnServer.departmentList(organizationId)
    ctx.send({departments})
})

/**
 * GET department by ID
 */
router.get('/departments/:id(\\d{1,20})', async function (ctx, next){
    const {id} = ctx.params
    const department = await fdnServer.departmentById(id)
    ctx.send({department})
})

/**
 * add department
 */
router.post('/organization/:organizationId(\\d{1,20})/department', async function (ctx, next) {
    const {organizationId} = ctx.params
    const {name} = ctx.request.body
    const department = await fdnServer.departmentCreate(name,organizationId)
    ctx.send({department})
})

/**
 *  modify department on fdn
 */
router.post('/departments/:id(\\d{1,20})', async function (ctx, next) {
    const {id} = ctx.params
    const {name, parentId} = ctx.request.body
    const department = await fdnServer.departmentMotify(name, parentId,id)
    ctx.send({department})
})

/**
 * delete department
 */
router.del('/departments/:id(\\d{1,20})', async function(ctx, next) {
    const {id} = ctx.params
    const department = await fdnServer.departmentRemove(id)
    ctx.send(department)
})


/**
 * department add alias
 */
router.post('/departments/:id(\\d{1,20})/alias', async function (ctx, next) {
    const {id} = ctx.params
    const {name} = ctx.request.body
    const alias = await fdnServer.departmentAliasCreate(id,name)
    ctx.send(alias)
})


/**
 * department remove alias
 */
router.del('/departments/:id(\\d{1,20})/alias/:aliasId(\\d{1,20})', async function (ctx, next) {
    const {id, aliasId} = ctx.params
    const alias = await fdnServer.departmentAliasRemove(id, aliasId)
    ctx.send(alias)
})


/**
 * ledger add tag
 */
router.post('/:ledgerId(\\d{1,20})/tag/:tagId(\\d{1,20})', async function(ctx, next) {
    const {ledgerId, tagId}= ctx.params
    const ledgerTag = await LedgerTag.create({
        ledger_id: ledgerId,
        tag_id: tagId
    })
    ctx.send({ledgerTag})
})

/**
 * ledger remove tag
 */
router.del('/:ledgerId(\\d{1,20})/tag/:tagId(\\d{1,20})', async function(ctx, next) {
    const {ledgerId, tagId} = ctx.params
    const ledgerTag = await LedgerTag.destroy({
        where:{
            ledger_id: ledgerId,
            tag_id: tagId
        }
    })
    ctx.send({ledgerTag})
})

/**
 * get tag (通用标签)
 */
router.get('/tags', async function(ctx, next) {
    const qs = {
        tagTypeId: 1
    }
    const tags = await fdnServer.getTags(qs)
    ctx.send(tags)
})




module.exports = router